Ahora que conocemos string::find y string::replace es buen momento para hacer una acción que reemplace todas las ocurrencias de una palabra por otra. Por ejemplo, si tenemos una frase:
string afirmacion; afirmacion += "El mundo se divide en tres tipos de personas:"; afirmacion += " los que saben contar y los que no";
nuestro objetivo es poder invocar una acción busca_y_reemplaza que al hacer:
busca_y_reemplaza(afirmacion, "tres", "dos");
cambie el trozo "tres" por "dos" en la frase. Además, lo debe hacer tantas veces como ocurra. Si la llamamos así:
busca_y_reemplaza(afirmacion, "los", "aquellos");
encontrará y reemplazará "los" 2 veces.
Para empezar con algo más fácil, vamos a hacer una función que muestre por pantalla en qué posiciones se encuentra la palabra que se busca, y que llamaremos simplemente busca. Así podremos comprobar que nuestra acción hace lo correcto antes de seguir.
La acción busca será así:
void busca(string frase, string palabra1) { int pos = frase.find(palabra1, 0); while (pos != -1) { cout << pos << ' '; pos = frase.find(palabra1, pos + 1); } cout << endl; }
Utilizamos una versión de string::find que recibe dos parámetros: el primero es la palabra que se busca y luego viene la posición inicial. La primera vez que llamamos a string::find, indicamos que queremos buscar a partir de la posición 0. Luego, si se ha encontrado algo en la posición pos, lo hacemos a partir de la siguiente (la pos + 1). Eso permite ir encontrando las diversas ocurrencias de la palabra1 en la frase.
Si tenemos el programa:
string f = "bla bla bla"; busca(f, "bla");
La salida será:
0 4 8
Una pequeña pregunta que dejo caer: en la acción busca, porqué piensas que ponemos pos + 1 y no pos + palabra1.size() cuando lo segundo parece más lógico??
Ahora, con el bucle montado, podemos aprovechar la información de dónde se encuentran las palabras encontradas para poner las que deben ir en su lugar. Ahora ya llamaremos a la acción busca_y_reemplaza y tendrá la cabecera final:
void busca_y_reemplaza(string& frase, string p1, string p2);
La frase pasa por referencia porque queremos que las modificaciones que hacemos queden reflejadas en la variable que nos pasan. La palabra p1 será la que buscamos y p2 la que reemplazará a p1. El código será, entonces:
void busca_y_reemplaza(string& frase, string p1, string p2) { int pos = frase.find(p1, 0); while (pos != -1) { frase.replace(pos, p1.size(), p2); pos = frase.find(p1, pos + 1); } }
Una parte interesante de esta acción es que para especificar el número de letras que hay que reemplazar, el segundo parámetro de string::replace es p1.size(), que es el tamaño de la palabra que hemos buscado, y por tanto la cantidad de letras que hay que quitar para poner p2.
Es interesante ver también que por el hecho de que la frase va cambiando durante los reemplazos, las posiciones que obteníamos antes con buscar pueden ser distintas que las que se usarán ahora para reemplazar.
La acción busca_y_reemplaza aún tiene un fallo. Recuerdas la pregunta que he dejado caer antes? Pues verás que tiene importancia. Con la acción tal como está ahora, si la frase es "bla bla" y quieres reemplazar "bla" por "habla" qué te parece que sucede?
En teoría el resultado sería "habla habla", pero en la práctica el programa entra en un bucle infinito (pruébalo). El problema es que cuando se hace el primer reemplazo, la frase que queda es "habla bla", y se empieza a buscar a partir de 1. Pero claro, la palabra "habla" contiene "bla" de forma que la nueva búsqueda devolverá 2. Entonces el nuevo reemplazo producirá "hahabla bla", el siguiente "hahahabla bla" y así hasta el infinito (y más allá!).
Si lo piensas, cada vez que insertas "habla" estás poniendo un nuevo "bla", de manera que nunca llegas al "segundo". La idea seria buscar a partir de la porción de frase que queda, que está después del reemplazo. Para hacer eso, el find interno del while debe ser:
pos = frase.find(p1, pos + p2.size());
Tenemos que saltar p2.size() posiciones a partir de pos para llegar a la parte que queda por buscar, porque acabamos de reemplazar p1 por p2.
En preparación